import rtmidi, time
from keyboard import is_pressed

# Set up MIDI output using python-rtmidi
channel = 0
midi_out = rtmidi.MidiOut()
midi_out.open_port(channel)  # Adjust the port number as needed

# Define the mapping between keys and MIDI notes
qwerty = "qazwsxedcrfvtgbyhnujmik,ol.p;/['"
key_to_note = {char: (index + 12 * (3 - (-1)) + 9)
               for index, char in enumerate(qwerty)}
for i, j in zip("234567890-", "zxcvbnm,./"):
    key_to_note[i] = key_to_note[j]
note_name = [' C', 'C#', ' D', 'Eb', ' E',
             ' F', 'F#', ' G', 'G#', ' A', 'Bb', ' B']
k2n = {key: note_name[note % 12] +
       str(note // 12 - 1) for key, note in key_to_note.items()}

# Keep track of pressed keys
pressed_keys = set()
sustained_keys = set()

# Keep track of whether the sustain pedal (spacebar) is pressed
sustain_pedal_pressed = False
bass_pressed = True

# Function to send MIDI messages and update the GUI


def send_midi_note(key, action, channel=channel):
    global bass_pressed
    actions = {"press": 0x90, "release": 0x80}
    act_code = actions[action] + channel
    note = key_to_note[key]
    if bass_pressed:
        note -= 24
    velocity = 64
    midi_out.send_message([act_code, note, velocity])

# Function to handle sustain pedal (spacebar) press and release
def on_sustain_pedal_press():
    global sustain_pedal_pressed
    if not sustain_pedal_pressed:
        sustain_pedal_pressed = True


def on_sustain_pedal_release():
    global sustain_pedal_pressed
    if sustain_pedal_pressed:
        sustain_pedal_pressed = False
        # Release all keys if sustain pedal is not pressed
        for key in sustained_keys.copy():
            send_midi_note(key, "release")
        sustained_keys.clear()


def on_bass_press():
    global bass_pressed
    if not bass_pressed:
        pressed_copy = pressed_keys.copy()
        for key in pressed_copy:
            on_key_release(key)
        bass_pressed = True
        for key in pressed_copy:
            on_key_press(key)


def on_bass_release():
    global bass_pressed
    if bass_pressed:
        pressed_copy = pressed_keys.copy()
        for key in pressed_copy:
            on_key_release(key)
        bass_pressed = False
        for key in pressed_copy:
            on_key_press(key)

# Bind key press and release events
def on_key_press(key):
    if key not in pressed_keys:
        send_midi_note(key, "press")
        pressed_keys.add(key)
        if key in sustained_keys:
            sustained_keys.remove(key)


def on_key_release(key):
    if key in pressed_keys:
        if sustain_pedal_pressed:
            sustained_keys.add(key)
        else:
            send_midi_note(key, "release")
        pressed_keys.remove(key)



# 把qwerty拆成三行
names = [[k2n[i] for i in qwerty[j::3]] for j in range(3)]
names_str = '\n'.join('  ' * j + '   '.join(names[j]) for j in range(3))

print('键盘布局基本如图所示。按住空格延音。按 ` 结束。')
print(names_str)

while not is_pressed('`'):
    if is_pressed(' '):
        on_sustain_pedal_press()
    else:
        on_sustain_pedal_release()
    if is_pressed('shift'):
        on_bass_press()
    else:
        on_bass_release()
    for char in key_to_note.keys():
        if is_pressed(char):
            on_key_press(char)
        else:
            on_key_release(char)
    time.sleep(1/32)

# Cleanup
input("接收输入字符，按回车结束。")
